package pattern.proxy;

import java.lang.reflect.Method;

import pattern.proxy.interceptor.DefaultInterceptor;
import pattern.proxy.interceptor.Interceptor;

public class ProxyTemplate<T> implements Proxy{
	
	private T actualObject;
	
	private Interceptor interceptor; 
	
	
	public ProxyTemplate(T actualObject) {
		
		this.actualObject = actualObject;
		this.interceptor = new DefaultInterceptor();
	}
	
	public ProxyTemplate( T actualObject, Interceptor interceptor){
		
		if(interceptor == null){
			this.interceptor = new DefaultInterceptor();
		}else{
			this.interceptor = interceptor;
		}
		this.actualObject = actualObject;
		
		
	}
	
	public Object invokeMethod(String methodName, Object[] args ) throws Throwable{
		
		Class clazz = actualObject.getClass();
		
		Class[] argClazzez = getArgsClazzez(args);
		
		Method method;
		
		try{
			method = clazz.getMethod(methodName, argClazzez);
		} catch (NoSuchMethodException e) {
			argClazzez = modifyArgClazzez(argClazzez);
			method = clazz.getMethod(methodName, argClazzez);
		}
		
		try {
			
			interceptor.before();
			Object returnValue = method.invoke(actualObject, args);
			interceptor.after();
			
			return returnValue;
		} catch (Throwable t) {
			interceptor.afterThrowing();
			throw t;
		} finally {
			interceptor.inFinally();
		}
		
	}
	
	private Class[] modifyArgClazzez(Class[] argClazzez) {
		
		Class[]tmp = new Class[argClazzez.length];
		
		for(int i =0; i<argClazzez.length; i++){
			Class c = argClazzez[i];
		
			if(c == Integer.class)
				c = int.class;
			else if(c == Double.class)
				c = double.class;
			else if(c == Boolean.class)
				c = boolean.class;
			else if(c == Byte.class)
				c = byte.class;
			else if(c == Short.class)
				c = short.class;
			else if(c == Long.class)
				c = long.class;
			else if(c == Float.class)
				c = float.class;
			else if(c == Character.class)
				c = char.class;
			
			tmp[i] = c;
		}
		return tmp;
	}

	private Class [] getArgsClazzez(Object[] arg){
		Class[] tmp = new Class[arg.length];

		for(int i=0; i< arg.length; i++){
			tmp[i] = arg[i].getClass();
		}
		
		return tmp;
	}

}
